www.gusucode.com > 基于Visual C++高级界面特效制作百例源码程序 > 基于Visual C++高级界面特效制作百例源码程序/code/char25/TemplateMFC/RegistryKey.cpp

    ///////////////////////////////////////////////////////////////////////////////
//
// File           : $Workfile:   RegistryKey.cpp  $
// Version        : $Revision:   1.5  $
// Function       : 
//
// Author         : $Author:   len  $
// Date           : $Date:   Oct 25 1998 11:24:38  $
//
// Notes          : 
//
// Modifications  :
//
// $Log:   G:/Documents/JetByte/Source/JetByteTools/Win32Tools/PVCS/RegistryKey.cpv  $
// 
//    Rev 1.5   Oct 25 1998 11:24:38   len
// Tidy up after running Lint...
// 
//    Rev 1.4   Oct 21 1998 20:08:52   len
// Bug fixes as reported by Steve Greenland - steve.greenland@aspentech.com
// 
//    Rev 1.3   Aug 27 1998 07:46:56   len
// Reference counted HKEY and loads of other things.
// 
//    Rev 1.2   Jun 06 1998 07:42:34   Len
// Made delete key reliable. Added DeleteKeyAndSubKeys.
// Tidied up.
// 
//    Rev 1.1   May 25 1998 11:02:40   Len
// Bug fixes.
// 
//    Rev 1.0   May 18 1998 07:49:24   Len
// Initial revision.
// 
///////////////////////////////////////////////////////////////////////////////
//
// Copyright 1998 JetByte Limited.
//
// JetByte Limited grants you ("Licensee") a non-exclusive, royalty free, 
// licence to use, modify and redistribute this software in source and binary 
// code form, provided that i) this copyright notice and licence appear on all 
// copies of the software; and ii) Licensee does not utilize the software in a 
// manner which is disparaging to JetByte Limited.
//
// This software is provided "AS IS," without a warranty of any kind. ALL
// EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING 
// ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
// OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. JETBYTE LIMITED AND ITS LICENSORS 
// SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 
// USING, MODIFYING OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO 
// EVENT WILL JETBYTE LIMITED BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, 
// OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE 
// DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING 
// OUT OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN IF JETBYTE LIMITED 
// HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
//
// This software is not designed or intended for use in on-line control of
// aircraft, air traffic, aircraft navigation or aircraft communications; or in
// the design, construction, operation or maintenance of any nuclear
// facility. Licensee represents and warrants that it will not use or
// redistribute the Software for such purposes.
//
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Include files
///////////////////////////////////////////////////////////////////////////////

#include "RegistryKey.hpp"

#include <tchar.h>
#include <stdio.h>
#include <malloc.h>

///////////////////////////////////////////////////////////////////////////////
// Namespace: JetByteTools
///////////////////////////////////////////////////////////////////////////////

namespace JetByteTools {

///////////////////////////////////////////////////////////////////////////////
// CRegistryKey
///////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
// Construction and destruction
///////////////////////////////////////////////////////////////////////////////

CRegistryKey::CRegistryKey(
   HKEY hKey) 
   :  m_pKey(NewCountedKey(hKey, true))
{
}

CRegistryKey::CRegistryKey(
   LPTSTR pRemoteMachine,
   HKEY hKey) 
   :  m_pKey(0)
{
   HKEY theKey = hKey;

   LONG result = RegConnectRegistry(pRemoteMachine, hKey, &theKey);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::CRegistryKey() - RegConnectRegistry"), result);
   }

   m_pKey = NewCountedKey(theKey, true);
}


CRegistryKey::CRegistryKey(
   HKEY hKey, 
   LPCTSTR pSubKey, 
   REGSAM samDesired /* = KEY_ALL_ACCESS */,
   LPTSTR pRemoteMachine /* = 0 */)
   :  m_pKey(0)
{
   HKEY theKey = hKey;

   // if we're passed a remote machine name...
   // do a connect registry first

   if (pRemoteMachine)
   {
      LONG result = RegConnectRegistry(pRemoteMachine, hKey, &theKey);

      if (ERROR_SUCCESS != result)
      {
         throw Exception(_T("CRegistryKey::CRegistryKey() - RegConnectRegistry"), result);
      }
   }

   HKEY newKey;

   LONG result = RegOpenKeyEx(theKey, pSubKey, 0, samDesired, &newKey);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::CRegistryKey(HKEY hKey ...)"), result);
   }

   m_pKey = NewCountedKey(newKey, true);
}

CRegistryKey::CRegistryKey(const CRegistryKey &rhs)
   :  m_pKey(rhs.m_pKey->AddRef())
{

}

CRegistryKey::~CRegistryKey()
{
   m_pKey = m_pKey->Release();
}

///////////////////////////////////////////////////////////////////////////////
// Assignment
///////////////////////////////////////////////////////////////////////////////

CRegistryKey &CRegistryKey::operator=(const CRegistryKey &rhs)
{
   if (this != &rhs)
   {
      CCountedRegKey *pNewKey = rhs.m_pKey->AddRef();
      
      m_pKey->Release();

      m_pKey = pNewKey;
   }

   return *this;
}

// NOTE: We OWN This hKey from this point on...

CRegistryKey &CRegistryKey::operator=(HKEY hKey)
{
   CCountedRegKey *pNewKey = NewCountedKey(hKey, true);
   
   m_pKey->Release();

   m_pKey = pNewKey;

   return *this;
}

///////////////////////////////////////////////////////////////////////////////
// Static helper function...
///////////////////////////////////////////////////////////////////////////////

CCountedRegKey *CRegistryKey::NewCountedKey(
   HKEY hKey, 
   bool bCloseKeyOnFailure /* = false */)
{
   CCountedRegKey *pCCountedRegKey = 0;

   try
   {
      pCCountedRegKey = new CCountedRegKey(hKey);
   }
   catch (...) //xalloc &e)
   {
      pCCountedRegKey = 0;
   }

   if (!pCCountedRegKey)
   {
      if (bCloseKeyOnFailure)
      {
         RegCloseKey(hKey);
      }

      throw Exception(_T("CRegistryKey::NewCCountedRegKey()"), 
         ERROR_NOT_ENOUGH_MEMORY);
   }

   return pCCountedRegKey;
}

///////////////////////////////////////////////////////////////////////////////
// Registry API wrappers...
///////////////////////////////////////////////////////////////////////////////

CRegistryKey CRegistryKey::OpenKey(
   LPCTSTR pSubKey, 
   REGSAM samDesired /* = KEY_ALL_ACCESS */) const
{
   return CRegistryKey(*this, pSubKey, samDesired);
}

void CRegistryKey::DeleteKey(
   LPCTSTR pKeyName) const 
{
   // Behaviour of RegDeleteKey differs on Win95 and NT
   // On 95 RegDeleteKey will delete keys with subkeys
   // on NT it wont.
   // To add some consistency to the world DeleteKey 
   // will always fail to delete a key with sub keys and
   // DeleteKeyAndSubKeys will always work...


   // scope the key...
   {
      CRegistryKey deadKey = OpenKey(pKeyName); 

      if (deadKey.BeginSubkeyIteration() != deadKey.EndSubkeyIteration())
      {
         throw Exception(_T("CRegistryKey::DeleteKey()"), ERROR_ACCESS_DENIED);
      }
   }

   LONG result = RegDeleteKey(m_pKey->GetCounted(), pKeyName);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::DeleteKey()"), result);
   }
}

void CRegistryKey::DeleteKeyAndSubkeys(
   LPCTSTR pKeyName) const 
{
   // Win95 doesn't need this as it deletes subkeys by default
   // NT wont delete a key with subkeys...

   // Scope the deadKey...
   {
      CRegistryKey deadKey = OpenKey(pKeyName);

		// This wont work, we are frigging with the sub keys of the key we're
		// iterating...

//      for (SubkeyIterator it = deadKey.BeginSubkeyIteration();
//         it != deadKey.EndSubkeyIteration();
//        ++it)
//      {
//         deadKey.DeleteKeyAndSubkeys(it.GetName());
//      }

      for (SubkeyIterator it = deadKey.BeginSubkeyIteration();
         it != deadKey.EndSubkeyIteration();
         it = deadKey.BeginSubkeyIteration())
      {
         deadKey.DeleteKeyAndSubkeys(it.GetName());
      }


   }

   DeleteKey(pKeyName);
}

CRegistryKey CRegistryKey::CreateKey(
   LPCTSTR pSubKey, 
   LPTSTR pClass /* = _T("") */, 
   DWORD dwOptions /* = REG_OPTION_NON_VOLATILE */, 
   REGSAM samDesired /* = KEY_ALL_ACCESS */,
   LPSECURITY_ATTRIBUTES pSecurityAttributes /* = NULL */) const
{
   DWORD disposition;

   CRegistryKey key = CreateOrOpenKey(
                        pSubKey,
                        &disposition,
                        pClass,
                        dwOptions,
                        samDesired,
                        pSecurityAttributes);
   
   if (disposition != REG_CREATED_NEW_KEY)
   {
      RegCloseKey(key);

      throw Exception(_T("CRegistryKey::CreateKey()"), ERROR_ALREADY_EXISTS);
   }


   return key;
}

CRegistryKey CRegistryKey::CreateOrOpenKey(
   LPCTSTR pSubKey, 
   DWORD *pDisposition /* = NULL */,
   LPTSTR pClass /* = _T("") */, 
   DWORD dwOptions /* = REG_OPTION_NON_VOLATILE */, 
   REGSAM samDesired /* = KEY_ALL_ACCESS */,
   LPSECURITY_ATTRIBUTES pSecurityAttributes /* = NULL */) const
{
   HKEY hKey;

   DWORD disposition;

   if (!pDisposition)
   {
      pDisposition = &disposition;
   }

   LONG result = RegCreateKeyEx(m_pKey->GetCounted(), pSubKey, 0, pClass, dwOptions,
                           samDesired, pSecurityAttributes, &hKey,
                           pDisposition);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::CreateOrOpenKey()"), result);
   }

   return CRegistryKey(hKey);
}

bool CRegistryKey::HasSubkey(
   LPCTSTR pSubKey,    
   REGSAM samDesired /* = KEY_ALL_ACCESS */) const
{
   bool hasKey = false;

   HKEY hKey;

   LONG result = RegOpenKeyEx(m_pKey->GetCounted(), pSubKey, 0, samDesired, &hKey);

   if (ERROR_SUCCESS == result)
   {
      hasKey = true;

      RegCloseKey(hKey);
   }
   else // should check for errors that mean no we dont have it, or we 
      // have it but not with the right access and throw if a real error
   {
      hasKey = false;
   }

   return hasKey;
}

CRegistryKey CRegistryKey::ConnectRegistry(LPTSTR pMachineName) const
{
   HKEY hKey;

   LONG result = RegConnectRegistry(pMachineName, m_pKey->GetCounted(), &hKey);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::ConnectRegistry()"), result);
   }

   return CRegistryKey(hKey);
}

void CRegistryKey::FlushKey() const
{
   LONG result = RegFlushKey(m_pKey->GetCounted());

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::FlushKey()"), result);
   }
}

CRegistryKey::operator HKEY() const 
{ 
   return m_pKey->GetCounted(); 
}

void CRegistryKey::DeleteValue(LPCTSTR pValueName) const
{
   LONG result = RegDeleteValue(m_pKey->GetCounted(), pValueName);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::DeleteValue()"), result);
   }
}

// Subkey iteration

CRegistryKey::SubkeyIterator CRegistryKey::BeginSubkeyIteration() const
{
   return SubkeyIterator(m_pKey);
}

CRegistryKey::SubkeyIterator CRegistryKey::EndSubkeyIteration() const
{
   return SubkeyIterator(0);
}
///////////////////////////////////////////////////////////////////////////////
// Value iteration
///////////////////////////////////////////////////////////////////////////////

CRegistryKey::ValueIterator CRegistryKey::BeginValueIteration() const
{
   return ValueIterator(m_pKey);
}

CRegistryKey::ValueIterator CRegistryKey::EndValueIteration() const
{
   return ValueIterator(0);
}

///////////////////////////////////////////////////////////////////////////////
// Query values
///////////////////////////////////////////////////////////////////////////////

bool CRegistryKey::QueryValue(LPCTSTR pValueName, LPBYTE *ppBytes) const
{
   DWORD dwType;
   LPBYTE pBuffer = 0;
   DWORD bufSize = 0;

   bool ok = false;

   LONG result = RegQueryValueEx(
      m_pKey->GetCounted(), 
      pValueName, 
      0,
      &dwType,
      pBuffer,
      &bufSize);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::QueryValue()"), result);
   }

   if (dwType == REG_BINARY || dwType == REG_NONE)
   {
      // bufSize should now tell us how much space we need...

		// TODO Smart pointer
		
      pBuffer = (LPBYTE)malloc(bufSize);

      result = RegQueryValueEx(
            m_pKey->GetCounted(), 
            pValueName, 
            0,
            &dwType,
            pBuffer,
            &bufSize);

      if (ERROR_SUCCESS == result)
      {
         ok = true;
         *ppBytes = pBuffer;
      }
      else
      {
         free(pBuffer); //crap

         throw Exception(_T("CRegistryKey::QueryValue()"), result);
      }
   }

   return ok;
}

bool CRegistryKey::QueryValue(LPCTSTR pValueName, LPCTSTR *ppString) const
{
   DWORD dwType;
   LPBYTE pBuffer = 0;
   DWORD bufSize = 0;

   bool ok = false;

   LONG result = RegQueryValueEx(
      m_pKey->GetCounted(), 
      pValueName, 
      0,
      &dwType,
      pBuffer,
      &bufSize);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::QueryValue()"), result);
   }

   if (dwType == REG_EXPAND_SZ || dwType == REG_SZ)
   {
      // bufSize should now tell us how much space we need...

      pBuffer = (LPBYTE)malloc(bufSize);

      result = RegQueryValueEx(
            m_pKey->GetCounted(), 
            pValueName, 
            0,
            &dwType,
            pBuffer,
            &bufSize);

      if (ERROR_SUCCESS == result)
      {
         ok = true;
         *ppString = (LPCTSTR)pBuffer;
      }
      else
      {
         free(pBuffer); //crap

         throw Exception(_T("CRegistryKey::QueryValue()"), result);
      }
   }

   return ok;
}

bool CRegistryKey::QueryValue(
   LPCTSTR pValueName, 
   DWORD &dwValue) const
{
   DWORD dwType;
   LPBYTE pBuffer = 0;
   DWORD bufSize = 0;

   bool ok = false;

   LONG result = RegQueryValueEx(
      m_pKey->GetCounted(), 
      pValueName, 
      0,
      &dwType,
      pBuffer,
      &bufSize);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::QueryValue()"), result);
   }

   if (dwType == REG_DWORD || 
       dwType == REG_DWORD_LITTLE_ENDIAN || 
       dwType == REG_DWORD_BIG_ENDIAN)
   {
      // bufSize should be sizeof(DWORD)
      
      if (bufSize != sizeof(DWORD))
      {
         throw Exception(_T("CRegistryKey::QueryValue()"), result);
      }

      result = RegQueryValueEx(
            m_pKey->GetCounted(), 
            pValueName, 
            0,
            &dwType,
            (LPBYTE)&dwValue,
            &bufSize);

      if (ERROR_SUCCESS != result)
      {
          throw Exception(_T("CRegistryKey::QueryValue()"), result);
      }
   }

   return ok;
}



// QueryValue for other types
// What about multiple values?

CRegistryKey::Value CRegistryKey::QueryValue(LPCTSTR pValueName /* = 0 */) const
{
   DWORD dwType;
   LPBYTE pBuffer = 0;
   DWORD bufSize = 0;

   LONG result = RegQueryValueEx(
      m_pKey->GetCounted(), 
      pValueName, 
      0,
      &dwType,
      pBuffer,
      &bufSize);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::QueryValue()"), result);
   }

   // bufSize should now tell us how much space we need...

   pBuffer = new unsigned char[bufSize];

   result = RegQueryValueEx(
         m_pKey->GetCounted(), 
         pValueName, 
         0,
         &dwType,
         pBuffer,
         &bufSize);

   if (ERROR_SUCCESS != result)
   {
      delete[] pBuffer; //crap

      throw Exception(_T("CRegistryKey::QueryValue()"), result);
   }

   return Value(pValueName, pBuffer, bufSize, dwType);
}






void CRegistryKey::SetValue(
    LPCTSTR pValueName, 
    LPBYTE pBytes, 
    DWORD cbBytes) const
{
   LONG result = RegSetValueEx(
      m_pKey->GetCounted(),
      pValueName,
      0,
      REG_BINARY,
      pBytes,
      cbBytes);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::SetValue()"), result);
   }
}

void CRegistryKey::SetValue(
   LPCTSTR pValueName, 
   LPCTSTR pValue, 
   DWORD dwType /*= REG_SZ*/) const
{
   if (dwType != REG_SZ &&
       //dwType != REG_LINK && 
       dwType != REG_EXPAND_SZ)
       // Handle MULTI_SZ and do the strlen ourselves?
   {
      throw Exception(_T("CRegistryKey::SetValue()"), ERROR_INVALID_FLAGS);
   }

   LONG result = RegSetValueEx(
      m_pKey->GetCounted(),
      pValueName,
      0,
      dwType,
      (LPBYTE)pValue,
      _tcslen(pValue) + sizeof(TCHAR));      // length of string plus null

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::SetValue()"), result);
   }
}

void CRegistryKey::SetValue(
   LPCTSTR pValueName, 
   DWORD value, 
   DWORD dwType /*= REG_DWORD*/) const
{
   if (dwType != REG_DWORD &&
       dwType != REG_DWORD_LITTLE_ENDIAN && 
       dwType != REG_DWORD_BIG_ENDIAN)
   {
      throw Exception(_T("CRegistryKey::SetValue()"), ERROR_INVALID_FLAGS);
   }

   LONG result = RegSetValueEx(
      m_pKey->GetCounted(),
      pValueName,
      0,
      dwType,
      (LPBYTE)&value,
      sizeof(DWORD));

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::SetValue()"), result);
   }
}

void CRegistryKey::SetValue(
   const Value &value) const
{
   // Should the value store the name too?
   LONG result = RegSetValueEx(
      m_pKey->GetCounted(),
      value.m_pName,
      0,
      value.m_dwType,
      value.m_pBuffer,
      value.m_bufSize);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::SetValue()"), result);
   }
}

void CRegistryKey::LoadKey(LPCTSTR pSubkeyName, LPCTSTR pFile) const
{
   LONG result = RegLoadKey(m_pKey->GetCounted(), pSubkeyName, pFile);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::LoadKey()"), result);
   }
}

void CRegistryKey::UnLoadKey(LPCTSTR pSubkeyName) const
{
   LONG result = RegUnLoadKey(m_pKey->GetCounted(), pSubkeyName);

   if (ERROR_SUCCESS == result)
   {
      throw Exception(_T("CRegistryKey::UnLoadKey()"), result);
   }
}

void CRegistryKey::SaveKey(
   LPCTSTR pFile, 
   LPSECURITY_ATTRIBUTES pSecurityAttributes /* = NULL */) const
{
   LONG result = RegSaveKey(m_pKey->GetCounted(), pFile, pSecurityAttributes);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::SaveKey()"), result);
   }
}

void CRegistryKey::RestoreKey(LPCTSTR pFile, DWORD flags /* = 0 */) const
{
   LONG result = RegRestoreKey(m_pKey->GetCounted(), pFile, flags);

   if (ERROR_SUCCESS == result)
   {
      throw Exception(_T("CRegistryKey::RestoreKey()"), result);
   }
}

void CRegistryKey::ReplaceKey(
   LPCTSTR pNewFile, 
   LPCTSTR pOldFile, 
   LPCTSTR pSubkeyName /* = 0 */) const
{
   LONG result = RegReplaceKey(m_pKey->GetCounted(), pSubkeyName, pNewFile, pOldFile);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::ReplaceKey()"), result);
   }
}

PSECURITY_DESCRIPTOR CRegistryKey::GetKeySecurity(
   SECURITY_INFORMATION securityInformation) const
{
   TExpandableBuffer<unsigned char> descriptor;

   DWORD dwSize = 0;
   
   LONG result = RegGetKeySecurity(m_pKey->GetCounted(), 
      securityInformation, 
      0,
      &dwSize);

   if (ERROR_INSUFFICIENT_BUFFER == result)
   {
      descriptor.Resize(dwSize);

      result = RegGetKeySecurity(m_pKey->GetCounted(), 
            securityInformation, 
            (PSECURITY_DESCRIPTOR)descriptor.GetBuffer(),
            &dwSize);
   }

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::GetKeySecurity()"), result);
   }

   return descriptor.ReleaseBuffer();
}


void CRegistryKey::SetKeySecurity(
   SECURITY_INFORMATION securityInformation,
   PSECURITY_DESCRIPTOR pSecurityDescriptor) const
{
   LONG result = RegSetKeySecurity(m_pKey->GetCounted(), 
      securityInformation, 
      pSecurityDescriptor);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::SetKeySecurity()"), result);
   }
}

void CRegistryKey::NotifyChangeKeyValue(
   HANDLE hEvent, 
   bool bSubKeys /* = false */,
   DWORD dwNotifyFilter /* = REG_NOTIFY_CHANGE_LAST_SET */) const
{
   LONG result = RegNotifyChangeKeyValue(m_pKey->GetCounted(),
      bSubKeys,
      dwNotifyFilter,
      hEvent,
      true);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::RegNotifyChangeKeyValue()"), result);
   }
}

void CRegistryKey::NotifyChangeKeyValue(
   bool bSubKeys /* = false */,
   DWORD dwNotifyFilter /* = REG_NOTIFY_CHANGE_LAST_SET */) const
{
   LONG result = RegNotifyChangeKeyValue(m_pKey->GetCounted(),
      bSubKeys,
      dwNotifyFilter,
      0,
      false);

   if (ERROR_SUCCESS != result)
   {
      throw Exception(_T("CRegistryKey::RegNotifyChangeKeyValue()"), result);
   }
}


///////////////////////////////////////////////////////////////////////////////
// CRegistryKey::SubkeyIterator
///////////////////////////////////////////////////////////////////////////////

CRegistryKey::SubkeyIteratorImpl::SubkeyIteratorImpl(CCountedRegKey *pKey) 
   :  CRegKeyIterator(pKey),
      m_Name(0),
      m_Class(0)
{
   if (pKey)
   {
      DWORD dwNumSubKeys = 0;
      DWORD dwMaxNameLen = 0;
      DWORD dwMaxClassLen = 0;

      LONG result = RegQueryInfoKey(
         pKey->GetCounted(),
         NULL,    // Not interested in this key's class 
         NULL,    // ditto
         NULL,    // Reserved
         &dwNumSubKeys,    
         &dwMaxNameLen,    
         &dwMaxClassLen,    
         NULL,    // Not interested in number of values 
         NULL,    // Not interested in max length of value name
         NULL,    // Not interested in max length of value buffer
         NULL,    // Not interested in length of security descriptor
         NULL);   // Not interested in last write time

      if (ERROR_SUCCESS != result)
      {
         throw Exception(_T("CRegistryKey::SubkeyIterator::SubkeyIterator()"),
            result);
      }

      if (0 != dwNumSubKeys)
      {
         // Allow for NULL character...

         m_Name.Resize(dwMaxNameLen + 1);
         m_Class.Resize(dwMaxClassLen + 1);
      }
   }
}

bool CRegistryKey::SubkeyIteratorImpl::operator==(const SubkeyIteratorImpl &rhs) const
{
   return CRegKeyIterator::operator==(rhs);
}

LPCTSTR CRegistryKey::SubkeyIteratorImpl::GetName() const
{
   return m_Name;
}

LPCTSTR CRegistryKey::SubkeyIteratorImpl::GetClass() const
{
   return m_Class;
}

CRegistryKey CRegistryKey::SubkeyIteratorImpl::OpenKey(
   REGSAM samDesired /*= KEY_ALL_ACCESS*/) const
{
   return CRegistryKey(m_pKey->GetCounted(), m_Name, samDesired);
}

bool CRegistryKey::SubkeyIteratorImpl::GetItem()
{
   FILETIME m_lastWriteTime;

   bool ok = true;
   bool done = false;

   // Could initialise with a call to GetKeyInfo to get longest key name etc

   while (!done && ok)
   {

      DWORD nameLen = m_Name.GetSize();
      DWORD classLen = m_Class.GetSize();

      LONG result = RegEnumKeyEx(
                     m_pKey->GetCounted(), 
                     m_index,
                     m_Name, 
                     &nameLen,
                     NULL,
                     m_Class,
                     &classLen,
                     &m_lastWriteTime);

      if (ERROR_NO_MORE_ITEMS == result)
      {
         ok = false;
      }
      else if (ERROR_MORE_DATA == result)
      {
         // Size has changed since we started 

         DWORD dwNumSubKeys = 0;
         DWORD dwMaxNameLen = 0;
         DWORD dwMaxClassLen = 0;

         result = RegQueryInfoKey(
            m_pKey->GetCounted(),
            NULL,    // Not interested in this key's class 
            NULL,    // ditto
            NULL,    // Reserved
            &dwNumSubKeys,    
            &dwMaxNameLen,    
            &dwMaxClassLen,    
            NULL,    // Not interested in number of values 
            NULL,    // Not interested in max length of value name
            NULL,    // Not interested in max length of value buffer
            NULL,    // Not interested in length of security descriptor
            NULL);   // Not interested in last write time

         if (ERROR_SUCCESS != result)
         {
            throw Exception(_T("CRegistryKey::SubkeyIterator::GetSubkeyInfo()"),
               result);
         }

         if (0 != dwNumSubKeys)
         {
            // Allow for NULL character...

            m_Name.Resize(dwMaxNameLen + 1);
            m_Class.Resize(dwMaxClassLen + 1);
         }
         else
         {
            done = true;
         }
      }
      else if (ERROR_SUCCESS != result)
      {
         throw Exception(_T("CRegistryKey::SubkeyIterator::GetSubkeyInfo()"),
            result);
      }
      else
      {
         done = true;
      }
   }
   return ok;
}
///////////////////////////////////////////////////////////////////////////////
// CRegistryKey::ValueIteratorImpl
///////////////////////////////////////////////////////////////////////////////

CRegistryKey::ValueIteratorImpl::ValueIteratorImpl(CCountedRegKey *pKey) 
   :  CRegKeyIterator(pKey),
      m_Name(0),
      m_Buffer(0),
		m_dwType(REG_NONE),
		m_dwBufUsed(0),
		m_pStringRep(0)
{
   if (m_pKey)
   {
      DWORD dwNumValues = 0;
      DWORD dwMaxValueNameLen = 0;
      DWORD dwMaxValueLen = 0;

      LONG result = RegQueryInfoKey(
         m_pKey->GetCounted(),
         NULL,    // Not interested in this key's class 
         NULL,    // ditto
         NULL,    // Reserved
         NULL,    // Not interested in number of sub keys
         NULL,    // Not interested in max sub key name length
         NULL,    // Not interested in max sub key class length
         &dwNumValues,
         &dwMaxValueNameLen,
         &dwMaxValueLen,
         NULL,    // Not interested in length of security descriptor
         NULL);   // Not interested in last write time

      if (ERROR_SUCCESS != result)
      {
         throw Exception(_T("CRegistryKey::ValueIterator::ValueIterator()"),
            result);
      }

      if (0 != dwNumValues)
      {
         // Allow for NULL character...

         m_Name.Resize(dwMaxValueNameLen + 1);
         m_Buffer.Resize(dwMaxValueLen + 1);
			m_dwBufUsed = 0;
			m_dwType = REG_NONE;
      }
   }
}

bool CRegistryKey::ValueIteratorImpl::operator==(
      const ValueIteratorImpl &rhs) const
{
   return CRegKeyIterator::operator==(rhs);
}

LPCTSTR CRegistryKey::ValueIteratorImpl::GetName() const
{
   return m_Name;
}

LPCTSTR CRegistryKey::ValueIteratorImpl::AsString() const
{
	if (!m_pStringRep)
	{
		m_pStringRep = Value::AsString(m_dwType, m_Buffer, m_dwBufUsed);
	}

	return m_pStringRep;
}

CRegistryKey::ValueIteratorImpl::operator CRegistryKey::Value() const
{
	return Value(m_Name, m_Buffer, m_dwBufUsed, m_dwType);
}

bool CRegistryKey::ValueIteratorImpl::GetItem()
{
   bool ok = true;
   bool done = false;

	// String representation cache is no longer valid

	delete[] m_pStringRep;
	m_pStringRep = 0;

   while (!done && ok)
   {

      DWORD nameLen = m_Name.GetSize();
      m_dwBufUsed = m_Buffer.GetSize();

      LONG result = RegEnumValue(
                     m_pKey->GetCounted(), 
                     m_index,
                     m_Name, 
                     &nameLen,
                     NULL,
                     &m_dwType,
                     m_Buffer,
                     &m_dwBufUsed);

      if (ERROR_NO_MORE_ITEMS == result)
      {
         ok = false;
      }
      else if (ERROR_MORE_DATA == result)
      {
         DWORD dwNumValues = 0;
         DWORD dwMaxValueNameLen = 0;
         DWORD dwMaxValueLen = 0;

         result = RegQueryInfoKey(
            m_pKey->GetCounted(),
            NULL,    // Not interested in this key's class 
            NULL,    // ditto
            NULL,    // Reserved
            NULL,    // Not interested in number of sub keys
            NULL,    // Not interested in max sub key name length
            NULL,    // Not interested in max sub key class length
            &dwNumValues,
            &dwMaxValueNameLen,
            &dwMaxValueLen,
            NULL,    // Not interested in length of security descriptor
            NULL);   // Not interested in last write time

         if (ERROR_SUCCESS != result)
         {
            throw Exception(_T("CRegistryKey::ValueIterator::GetValueInfo()"),
               result);
         }

         if (0 != dwNumValues)
         {
            // Allow for NULL character...

            m_Name.Resize(dwMaxValueNameLen + 1);
            m_Buffer.Resize(dwMaxValueLen + 1);
         }
         else
         {
            done = true;
         }
      }
      else if (ERROR_SUCCESS != result)
      {
         throw Exception(_T("CRegistryKey::ValueIterator::GetValueInfo()"),
            result);
      }
      else
      {
         done = true;
      }
   }
   return ok;
}

///////////////////////////////////////////////////////////////////////////////
// CRegistryKey::Exception
///////////////////////////////////////////////////////////////////////////////

CRegistryKey::Exception::Exception(
   const LPCTSTR pWhere, 
   LONG error)
   :  CWin32Exception(pWhere, (DWORD)error)
{
}

///////////////////////////////////////////////////////////////////////////////
// CRegistryKey::Value
///////////////////////////////////////////////////////////////////////////////

CRegistryKey::Value::Value(
   LPCTSTR pName, 
   const LPBYTE pBuffer, 
   DWORD bufSize, 
   DWORD dwType /*= REG_BINARY*/)
   :  m_pName((LPTSTR)_tcsdup(pName)),
      m_dwType(dwType), 
      m_pBuffer((LPBYTE)duplicateBuffer(pBuffer, bufSize)), 
      m_bufSize(bufSize),
      m_pStringRep(0)
{
}

CRegistryKey::Value::Value(
   LPCTSTR pName, 
   LPCTSTR pString, 
   DWORD dwType /*= REG_SZ */)
   :  m_pName((LPTSTR)_tcsdup(pName)),
      m_dwType(dwType),
      m_pBuffer((LPBYTE)_tcsdup(pString)),
      m_bufSize(_tcslen(pString)),
      m_pStringRep(0)
{
   if (m_dwType != REG_EXPAND_SZ && 
       m_dwType != REG_SZ)
   {
      throw Exception();
   }
}

CRegistryKey::Value::Value(
   LPCTSTR pName, 
   DWORD dwValue, 
   DWORD dwType /*= REG_DWORD*/)
   :  m_pName((LPTSTR)_tcsdup(pName)),
      m_dwType(dwType),
      m_pBuffer(new unsigned char[sizeof(DWORD)]),
      m_bufSize(sizeof(DWORD)),
      m_pStringRep(0)
{
   if (dwType != REG_DWORD &&
       dwType != REG_DWORD_LITTLE_ENDIAN && 
       dwType != REG_DWORD_BIG_ENDIAN)
   {
      throw Exception();
   }

   memcpy(m_pBuffer, &dwValue, sizeof(DWORD));
}

CRegistryKey::Value::Value(const Value &rhs)
   :  m_pName(_tcsdup(rhs.m_pName)), 
      m_dwType(rhs.m_dwType),
      m_pBuffer(rhs.duplicateBuffer()),
      m_bufSize(rhs.m_bufSize),
      m_pStringRep(0)
{
   memcpy(m_pBuffer, rhs.m_pBuffer, m_bufSize);
}

CRegistryKey::Value::~Value()
{
   delete[] m_pName;
   delete[] m_pBuffer;
   delete[] m_pStringRep;
}

CRegistryKey::Value &CRegistryKey::Value::operator=(const Value &rhs)
{
   if (this != &rhs)
   {
      LPBYTE pNewBuffer = rhs.duplicateBuffer();
      LPBYTE pOldBuffer = m_pBuffer;
	  
      LPTSTR pNewName = _tcsdup(rhs.m_pName);
      LPTSTR pOldName = m_pName;

      m_dwType = rhs.m_dwType;
      m_bufSize = rhs.m_bufSize;

      m_pBuffer = pNewBuffer;
      m_pName = pNewName;

      delete[] pOldBuffer;
      delete[] pOldName;

      delete[] m_pStringRep;
      m_pStringRep = 0;
   }

   return *this;
}

CRegistryKey::Value::operator LPBYTE() const
{
   if (m_dwType != REG_BINARY && 
       m_dwType != REG_NONE)
   {
      throw Exception();
   }

   return m_pBuffer;
}

CRegistryKey::Value::operator LPCTSTR() const
{
   if (m_dwType != REG_EXPAND_SZ && 
       m_dwType != REG_SZ)
   {
      throw Exception();
   }

   return (LPCTSTR)m_pBuffer;
}

CRegistryKey::Value::operator DWORD() const
{
   if (m_dwType != REG_DWORD &&
       m_dwType != REG_DWORD_LITTLE_ENDIAN && 
       m_dwType != REG_DWORD_BIG_ENDIAN)
   {
      throw Exception();
   }

   return *(DWORD*)m_pBuffer;
}

LPCTSTR CRegistryKey::Value::Name() const
{
	return m_pName;
}

LPCTSTR CRegistryKey::Value::AsString() const
{
	if (!m_pStringRep)
	{
		m_pStringRep = AsString(m_dwType, m_pBuffer, m_bufSize);
	}

	return m_pStringRep;
}

LPTSTR CRegistryKey::Value::AsString(
	DWORD dwType, 
	const LPBYTE pBuffer, 
	DWORD bufSize)
{
	LPTSTR pStringRep;

   // TODO Convert all reps to a string rep

   if (dwType == REG_EXPAND_SZ || 
       dwType == REG_SZ)
   {
      // Already a string!

      pStringRep = _tcsdup((LPTSTR)pBuffer);
   }
	else if (dwType == REG_MULTI_SZ)
	{
		// multiple strings...

		// Should strip the \0's out...
		pStringRep = _tcsdup((LPTSTR)pBuffer);
	}
	else if (dwType == REG_BINARY || 
			   dwType == REG_NONE)
   {
		pStringRep = bytesAsString(pBuffer, bufSize);
   }
   else if (dwType == REG_DWORD ||
            dwType == REG_DWORD_LITTLE_ENDIAN || 
            dwType == REG_DWORD_BIG_ENDIAN)
   {
      pStringRep = _tcsdup(_T("A number"));
   }
	else
	{
		// Anything else...
      pStringRep = bytesAsString(pBuffer, bufSize);
	}

   return pStringRep;
}

LPTSTR CRegistryKey::Value::bytesAsString(
	const LPBYTE pBuffer, 
	DWORD bufSize)
{
	LPTSTR pStringRep = new TCHAR[(bufSize * 2) + 1];
	
	LPTSTR pHere = pStringRep;
	LPBYTE pBuf = pBuffer;

	for (DWORD i = 0; i < bufSize; i++)
	{
		_stprintf(pHere, _T("%2.2x"), *pBuf);

		pHere += 2;
		pBuf++;
	}

	return pStringRep;
}

LPBYTE CRegistryKey::Value::duplicateBuffer() const
{
	return duplicateBuffer(m_pBuffer, m_bufSize);
}

LPBYTE CRegistryKey::Value::duplicateBuffer(LPBYTE pBuffer, DWORD bufSize) const
{
	LPBYTE pNewBuffer = new unsigned char[bufSize];
	memcpy(pNewBuffer, pBuffer, bufSize);
	
	return pNewBuffer;
}


CRegistryKey::Value::Exception::Exception()
   :  CRegistryKey::Exception(_T("CRegistryKey::Value::Exception"),
         ERROR_INVALID_PARAMETER)
{
}

///////////////////////////////////////////////////////////////////////////////
// Namespace: JetByteTools
///////////////////////////////////////////////////////////////////////////////

} // End of namespace JetByteTools 

///////////////////////////////////////////////////////////////////////////////
// End of file...
///////////////////////////////////////////////////////////////////////////////